package runnerworm.core;

import java.awt.*;
import javax.swing.*;
import java.io.*;
import runnerworm.ui.*;

/**
 * <p>Title: Engine</p>
 * <p>Description: This is what make things work. The real connection between
 * the user interface and the game itself. It will hold the worm, the background
 * and the class that will know if something happened or not. I may say this
 * would be the administrator class. :]</p>
 * @author Guilherme Mauro Germoglio Barbosa
 */

public class Engine
    implements BeholderListener, UserInterfaceListener {

  /**
   * This will be where everything will be shown.
   */
  private JPanel panel;
  /**
   * Where the score will be shown.
   */
  private JLabel scoreLabel;
  /**
   * The object that will launch AvailableEvents.
   */
  private AvailableEventLauncher availableEventLauncher;
  /**
   * The object that observes everything and launches events about what is
   * happening.
   */
  private Beholder beholder;
  /**
   * The game's available options.
   */
  private GameOptions gameOptions;
  /**
   * The game's high scores.
   */
  private HighScores highScores;
  /**
   * The game's main character, the worm!
   */
  private Worm worm;
  /**
   * Where the worm is going to be! The tunnel!
   */
  private Tunnel tunnel;
  /**
   * The real user's score.
   */
  private double score;
  /**
   * The player's name.
   */
  private String name;
  /**
   * The worm's velocity.
   */
  private int wormVelocity;
  /**
   * The score's increaser.
   */
  private double scoreIncreaser;
  /**
   * The score's increaser for easy level.
   */
  private final double EASY_LEVEL_INCREASER = 0.5;
  /**
   * The score's increaser for medium level.
   */
  private final double MEDIUM_LEVEL_INCREASER = 3;
  /**
   * The score's increaser for hard level.
   */
  private final double HARD_LEVEL_INCREASER = 5;
  /**
   * The system's line separator.
   */
  private final String lineSeparator = System.getProperty("line.separator");
  /**
   * The system's file separator.
   */
  private final String fileSeparator = System.getProperty("file.separator");



  /**
   * It will create the availableEventLauncher, the high scores.
   */
  public Engine() {
    availableEventLauncher = new AvailableEventLauncher();
    try {
      highScores = new HighScores();
    } catch (Exception e) {
      showException(e);
    }
  }

  /**
   * Will add an EngineListener to the AvailableEventLauncher.
   * @param l The EngineListener.
   */
  public void addEngineListener(EngineListener l) {
    availableEventLauncher.addEngineListener(l);
  }

  /**
   * Do what is to be done when "Quit" is selected.
   * @param evt The QuitGameEvent captured.
   */
  public void quitSelected(QuitGameEvent evt) {
    try {
      highScores.recordInDisk();
    } catch (Exception e) {
      showException(e);
    }
    System.exit(0);
  }
  /**
   * Will implement what the Engine will have to do when the
   * "new game" option is selected.
   * @param gameOptions The option's needed to start the game.
   * @param score The game's score label.
   * @param panel Where the game will be shown.
   */
  public void newGameSelected(GameOptions gameOptions, JLabel score, JPanel panel) {
    // if there's a game running, stops it.
    if (worm != null)
      endGame();
    // refreshes the atributes according to the user interface.
    this.gameOptions = gameOptions;
    this.scoreLabel = score;
    this.panel = panel;
    this.score = 0;
    // sets the worm velocity according to the game level
    wormVelocity = gameOptions.getCurrentLevel();
    // sets the score increaser
    setIncreaser(gameOptions.getCurrentLevel());
    // creates a new worm
    worm = new Worm(30,30);
    WormThread wormThread = new WormThread(worm, panel.getGraphics());
    wormThread.start();
    // creates a new tunnel
    tunnel = new Tunnel();
    TunnelThread tunnelThread = new TunnelThread(tunnel, wormVelocity, panel.getGraphics());
    tunnelThread.start();
    // creates a new beholder
    beholder = new Beholder(worm, tunnel);
    beholder.addBeholderListener(this);
    beholder.start();
  }

  /**
   * Sets the score increaser.
   * @param level An integer representing the game level.
   */
  private void setIncreaser(int level) {
    switch (level) {
      case GameOptions.EASY_LEVEL:
        scoreIncreaser = EASY_LEVEL_INCREASER;
        break;
      case GameOptions.MEDIUM_LEVEL:
        scoreIncreaser = MEDIUM_LEVEL_INCREASER;
        break;
      case GameOptions.HARD_LEVEL:
        scoreIncreaser = HARD_LEVEL_INCREASER;
        break;
    }
  }

  /**
   * Will implement what the Engine will have to do when the
   * "options" option is selected.
   * @param evt The OptionsEvent captured.
   */
  public void optionsSelected(OptionsEvent evt) {
    availableEventLauncher.createOptionsAvailableEvent(gameOptions==null?new GameOptions():gameOptions);
  }

  /**
   * Will implement what the Engine will have to do when the
   * "high scores" option is selected.
   * @param evt The HighScoresEvent captured.
   */
  public void highScoresSelected(HighScoresEvent evt) {
    availableEventLauncher.createHighScoresAvailableEvent(highScores);
  }

  /**
   * Will implement what the Engine will have to do when the
   * "help" option is selected.
   * @param evt The HelpEvent captured.
   */
  public void helpSelected(HelpEvent evt) {
    availableEventLauncher.createHelpAvailableEvent(getHelp());
  }

  /**
   * Will implement what the Engine will have to do when the
   * "about" option is selected.
   * @param evt The AboutEvent captured.
   */
  public void aboutSelected(AboutEvent evt) {
    availableEventLauncher.createAboutAvailableEvent(getAbout());
  }

  /**
   * Will implement what Engine will have to do when the worm collide.
   * @param evt The WormCollisionBeholderEvent captured.
   */
  public void wormHasCollided(WormCollisionBeholderEvent evt) {
    // kills the worm
    worm.killWorm();
    // stops the tunnel
    tunnel.stopTunnel();
    // checks if the current score can be at the hall of fame
    checkHighScores(score);
  }

  /**
   * Will create another tunnel to keep the worm running and letting the game a
   * little harder.
   * @param evt The TunnelEndedBeholderEvent captured.
   */
  public void tunnelHasEnded(TunnelEndedBeholderEvent evt) {
    // stops the current tunnel
    tunnel.stopTunnel();
    // increases the worm velocity
    wormVelocity++;
    // creates a new tunnel
    tunnel = new Tunnel();
    TunnelThread tunnelThread = new TunnelThread(tunnel, wormVelocity, panel.getGraphics());
    tunnelThread.start();
    // creates a new beholder
    beholder = new Beholder(worm, tunnel);
    beholder.addBeholderListener(this);
    beholder.start();
  }

  /**
   * Will make points.
   * @param evt The event that represents the points.
   */
  public void hasScored(PointScoredBeholderEvent evt) {
    score += scoreIncreaser;
    scoreLabel.setText(Double.toString(score));
  }


  /**
   * Returns a string containing a text.
   * @param textType The type text, such "about" ou "help"
   * @return The text.
   */
  private String getText(String textType) {
    String returnString = "";
    String line = "";
    try {
      BufferedReader in = new BufferedReader(new FileReader("runnerworm" +
          fileSeparator + "core" + fileSeparator + textType + ".wrm"));
      // reads the file
      while ( (line = in.readLine()) != null) {
        returnString += line + lineSeparator;
      }
    }
    catch (FileNotFoundException e) {
      // if there's no file
      return "No " + textType + " text found.";
    }
    catch (IOException f) {
      // if some shit happens
      showException(f);
      return returnString;
    }
    return returnString;
  }

  /**
   * Returns a string containing the about text.
   * @return The about text.
   */
  private String getAbout() {
    return getText("about");
  }

  /**
   * Returns a string containing the help text.
   * @return The help text.
   */
  private String getHelp() {
    return getText("help");
  }


  /**
   * It will notify the user that a exception was caught.
   * @param e The exception.
   */
  private void showException(Exception e) {
    availableEventLauncher.createExceptionAvailableEvent(e);
  }

  /**
   * Will make the worm goes up.
   */
  public void wormGoesUp() {
    if (worm != null)
      worm.setGoingUp(true);
  }

  /**
   * Will make the worm goes down.
   */
  public void wormGoesDown() {
    if (worm != null)
      worm.setGoingUp(false);
  }

  /**
   * Sets the player's name. And adds it to the highScores.
   * @param playerName The player's name.
   */
  public void setPlayerName(String playerName) {
    this.name = playerName;
    highScores.add(new HighScore(name, score));
    try {
      highScores.recordInDisk();
    }
    catch (Exception e) {
      showException(e);
    }
  }

  /**
   * Checks if the score can be at the high scores - and if so, put it there.
   * @param score The score.
   */
  private void checkHighScores(double score) {
    if (highScores.canBeAtHallOfFame(score))
      availableEventLauncher.createAskForPlayerNameEvent();
  }

  /**
   * Will end the game, killing the threads.
   */
  private void endGame() {
    // kills the worm
    worm.killWorm();
    // stops the tunnel
    tunnel.stopTunnel();
  }

}

